const EventEmitter = require("events");
const fs = require("fs");
class WriteStream extends EventEmitter {
  constructor(path, options = {}) {
    super();
    this.path = path;
    this.flags = options.flags || "w";
    this.autoClose = options.autoClose || true;
    this.emitClose = options.emitClose || true;
    this.start = options.start || 0;
    this.highWaterMark = options.highWaterMark || 16 * 1024;
    this.encoding = options.encoding || "utf8";

    this.offset = this.start; // offset 是写入的偏移量，这个参数可变
    this.writing = false; // 是否正在写入，如果是正在写入，后续的写入操作要当到队列中
    this.cache = []; // 缓存取， 队列
    this.needDrain = false; // 只有写入的个数 达到了highWaterMark 并且清空后才会将这个值变成true
    this.len = 0; // 每次写入的时候统计写入的个数

    this.open();
  }
  open() {
    fs.open(this.path, this.flags, (err, fd) => {
      this.fd = fd;
      this.emit("open", this.fd); // 发射open事件
    });
  }
  // 写入的数据只能是是字符串或者buffer
  clear() {
    let buffer = this.cache.shift(); // 拿出缓存区中的第一个来
    // 这个浪费性能，内部用的是链表
    if (buffer) {
      let { chunk, encoding, clearBuffer } = buffer;
      this._write(chunk, encoding, clearBuffer);
    } else {
      this.writing = false;
      if (this.needDrain) {
        this.emit("drain");
      }
    }
  }
  end(chunk = "", encoding = this.encoding, callback = () => {}) {
    this.write(chunk, encoding, () => {
      callback();
      fs.close(this.fd, () => {
        if (this.emitClose) {
          this.emit("close");
        }
      });
    });
  }
  write(chunk, encoding = this.encoding, callback = () => {}) {
    chunk = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
    this.len += chunk.length; // 每次写入的个数都要记录，看能不能拿到highWaterMark
    this.needDrain = this.len >= this.highWaterMark; // 说明写入的个数达到了触发drain条件

    const clearBuffer = () => {
      callback(); // 当前本次写入完成后回调,在调用后续的写入操作
      this.clear();
    };
    if (this.writing) {
      this.cache.push({
        chunk,
        encoding,
        clearBuffer,
      });
    } else {
      // 第一次做的写入操作， 这个时候需要像文件中写入
      this.writing = true;
      this._write(chunk, encoding, clearBuffer);
    }
    return !this.needDrain;
  }
  _write(chunk, encoding, callback) {
    if (typeof this.fd !== "number") {
      return this.once("open", () => this._write(chunk, encoding, callback));
    }
    fs.write(this.fd, chunk, 0, chunk.length, this.offset, (err, written) => {
      this.offset += written;
      this.len -= written;
      callback();
    });
  }
}
module.exports = WriteStream;
